﻿using System;
using System.Collections.Generic;
using System.Linq;
using FNN.CustomFunctions;
using FNN.NetworkComponents;
using FNN.Genetic;

namespace FNN.Networks
{
    [Serializable]
    public class RegularNetwork : Network
    {
        protected int SuccessCountValue;

        /// <summary>
        ///     Количество правильных ответов
        /// </summary>
        public int SuccessCount
        {
            get { return SuccessCountValue; }
            set { SuccessCountValue = value; }
        }

        /// <summary>
        ///     Конструктор с параметрами
        /// </summary>
        /// <param name="inputCount">Количество входов</param>
        /// <param name="layerCount">Количество слоев</param>
        /// <param name="layerNeuronsCount">Количество нейронов в слое</param>
        /// <param name="functions">Активационные функции</param>
        public RegularNetwork(int inputCount, int layerCount, int layerNeuronsCount, List<FunctionTypes> functions)
        {
            InputCountValue = inputCount;
            LayerCountValue = layerCount;
            NeuronInLayerValue = layerNeuronsCount;
            SuccessCount = 0;

            LayersList = new List<Layer>();
            SynapsesList = new List<Synapse>();
            LayersList.Add(new Layer(0, inputCount, true, functions));

            for (var i = 1; i < layerCount; i++)
            {
                LayersList.Add(new Layer(i + 1, layerNeuronsCount, false, functions));
                foreach (var inputNeuron in LayersList[i - 1].Neurons)
                {
                    foreach (var outputNeuron in LayersList[i].Neurons)
                    {
                        SynapsesList.Add(new Synapse(RndNumber.NextDouble()*(-0.1) + 0.05, inputNeuron, outputNeuron));
                    }
                }
            }
        }

        /// <summary>
        ///     Конструктор с количеством выходов
        /// </summary>
        /// <param name="inputCount">Количество входов</param>
        /// <param name="layerCount">Количество слоев</param>
        /// <param name="layerNeuronsCount">Количество нейронов в слое</param>
        /// <param name="functions">Активационные функции</param>
        /// <param name="outputCount">Количество выходов</param>
        public RegularNetwork(int inputCount, int layerCount, int layerNeuronsCount, List<FunctionTypes> functions,
            int outputCount)
        {
            InputCountValue = inputCount;
            LayerCountValue = layerCount;
            OutputCountValue = outputCount;
            NeuronInLayerValue = layerNeuronsCount;
            SuccessCountValue = 0;

            LayersList = new List<Layer>();
            SynapsesList = new List<Synapse>();
            LayersList.Add(new Layer(1, inputCount, true, functions));
            //TODO: Оптимизировать цикл
            for (var i = 1; i < layerCount; i++)
            {
                LayersList.Add(new Layer(i + 1, layerNeuronsCount, false, functions));
                foreach (var inputNeuron in LayersList[i - 1].Neurons)
                {
                    foreach (var outputNeuron in LayersList[i].Neurons)
                    {
                        SynapsesList.Add(new Synapse(RndNumber.NextDouble()*(-0.1) + 0.05, inputNeuron, outputNeuron));
                    }
                }
            }

            var lastLayer = new Layer(layerCount + 1, outputCount, true, functions);
            foreach (var inputNeuron in LayersList.Last().Neurons)
            {
                foreach (var outputNeuron in lastLayer.Neurons)
                {
                    SynapsesList.Add(new Synapse(RndNumber.NextDouble()*(-0.1) + 0.05, inputNeuron, outputNeuron));
                }
            }
            LayersList.Add(lastLayer);
        }

        /// <summary>
        ///     Конструктор на основе структуры
        /// </summary>
        /// <param name="sourceStructure">Структура сети</param>
        public RegularNetwork(NetworkStructure sourceStructure)
        {
            Random randomizer = new Random();
            InputCountValue = sourceStructure.LayerStructures.ElementAt(0).FunctionsList.Count();
            LayerCountValue = sourceStructure.LayerStructures.Count();
            NeuronInLayerValue = sourceStructure.LayerStructures.ElementAt(1).FunctionsList.Count();

            LayersList = new List<Layer>();
            SynapsesList = new List<Synapse>();

            for (int i = 0; i < sourceStructure.LayerStructures.Count(); i++)
            {
                var nextLayer = new Layer() { LayerId = i + 1 };
                for (int k = 0; k < sourceStructure.LayerStructures.ElementAt(0).FunctionsList.Count(); k++)
                {
                    nextLayer.Neurons.Add(new Neuron((FunctionTypes)sourceStructure.LayerStructures.ElementAt(0).FunctionsList[k]) { NeuronId = (i + 1) * 1000 + k });
                }
                LayersList.Add(nextLayer);
            }

            for (int i = 0; i < sourceStructure.SynapseStructures.Count(); i++)
            {
                var nextInputId = sourceStructure.SynapseStructures.ElementAt(i).InputNeuronId;
                var nextOutputId = sourceStructure.SynapseStructures.ElementAt(i).OutputNeuronId;

                var inputLayerIndex = nextInputId / 1000 - 1;
                var outputLayerIndex = nextOutputId / 1000 - 1;
                var inputNeuronIndex = nextInputId % 1000;
                var outputNeuronIndex = nextOutputId % 1000;

                Neuron inputNeuron = null, outputNeuron = null;

                if (LayersList.ElementAt(inputLayerIndex) != null)
                {
                    inputNeuron = LayersList.ElementAt(inputLayerIndex).Neurons.ElementAt(inputNeuronIndex);
                }

                if (LayersList.ElementAt(outputLayerIndex) != null)
                {
                    outputNeuron = LayersList.ElementAt(outputLayerIndex).Neurons.ElementAt(outputNeuronIndex);
                }

                if (inputNeuron == null)
                {
                    var randomLayer = randomizer.Next(0, LayersList.Count());
                    inputNeuron = LayersList.ElementAt(randomLayer).Neurons.ElementAt(randomizer.Next(0, LayersList.ElementAt(randomLayer).Neurons.Count()));
                }

                if (outputNeuron == null)
                {
                    var randomLayer = randomizer.Next(0, LayersList.Count());
                    outputNeuron = LayersList.ElementAt(randomLayer).Neurons.ElementAt(randomizer.Next(0, LayersList.ElementAt(randomLayer).Neurons.Count()));
                }

                SynapsesList.Add(new Synapse(randomizer.NextDouble() * (-0.1) + 0.05, inputNeuron, outputNeuron));
            }
        }

        /// <summary>
        ///     Конструктор для создания сети со случайными параметрами
        /// </summary>
        public RegularNetwork()
        {
            var randomizer = new Random();
            InputCountValue = randomizer.Next(50, 200);
            LayerCountValue = randomizer.Next(1, 10);
            NeuronInLayerValue = -1;
            SuccessCountValue = 0;

            LayersList = new List<Layer>();
            SynapsesList = new List<Synapse>();
            LayersList.Add(new Layer(1, InputCountValue, true));

            for (var i = 1; i < LayerCountValue; i++)
            {
                var layerNeuronCount = randomizer.Next(2, 32);
                LayersList.Add(new Layer(i + 1, layerNeuronCount, false));
                foreach (var inputNeuron in LayersList[i - 1].Neurons)
                {
                    foreach (var outputNeuron in LayersList[i].Neurons)
                    {
                        SynapsesList.Add(new Synapse(RndNumber.NextDouble()*(-0.1) + 0.05, inputNeuron, outputNeuron));
                    }
                }
            }
        }

        /// <summary>
        ///     Конструктор копирования
        /// </summary>
        /// <param name="source">Исходный экземпляр</param>
        public RegularNetwork(RegularNetwork source)
        {
            InputCountValue = source.InputCountValue;
            LayerCountValue = source.LayerCountValue;
            NeuronInLayerValue = source.NeuronInLayerValue;
            SuccessCountValue = source.SuccessCountValue;

            LayersList = new List<Layer>();
            SynapsesList = new List<Synapse>();

            foreach (var layer in source.LayersList)
            {
                LayersList.Add(new Layer(layer));
            }

            foreach (var synapse in source.SynapsesList)
            {
                SynapsesList.Add(new Synapse(synapse));
            }
        }
    }
}
